﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using FingerTracking;
using fingerWinForm.Excel;
using fingerWinForm.GUI;

namespace fingerWinForm.SVM
{

    public struct TrainAndPredictForSVM
    {
        public String TrainPath;
        public String PredictPath;
    }

    public struct LocalSVMParameters
    {
        public double G;
        public double C;
        public double AverageAccuracy;
    }

    class SVMManager
    {
        private List<TrainAndPredictForSVM> SVMFilesList;
        private MainWindow.TestName currentTestName;
        private Int32[] gradesArrayFromOutFile;
        private Int32[] gradesArrayFromPredictFile;
        private LocalSVMParameters[] arrayOfSVMParameters;
        private LocalSVMParameters maxAverageAccuracy;
        private SVMWindow.SVMParameters svmParameters;
        private double[,] matrixForComparingGrades;
        private double numOfVectorsToPredict;
        private double[,] confusionMatrixForCandGamma;
        private double[,] tempConfusionMatrixForCandGamma;
        private double[] sumGradeForConfusionMatrixForCandGamma;
        private double[] tempSumGradeForConfusionMatrixForCandGamma;
        private SVMResultsWindow svmResultsWindow;
        private SVMResultsWindow_ThreeClasses svmResultsWindowThreeClasses;
        private double[] sumGradesInPredictFile;
        private int numOfClasses;
        public delegate void updatePrograssBar(int progressBarStep);
        public event updatePrograssBar m_updatePrograssBar;

        

        public SVMManager()
        {
            SVMFilesList = new List<TrainAndPredictForSVM>();
            currentTestName = new MainWindow.TestName();
            gradesArrayFromOutFile = new Int32[20];
            gradesArrayFromPredictFile = new Int32[20];
            svmParameters = new SVMWindow.SVMParameters();
            svmResultsWindowThreeClasses = new SVMResultsWindow_ThreeClasses();
            matrixForComparingGrades = new double[5,5];
            confusionMatrixForCandGamma = new double[5,5];
            tempConfusionMatrixForCandGamma = new double[5,5];
            sumGradeForConfusionMatrixForCandGamma = new double[5];
            tempSumGradeForConfusionMatrixForCandGamma = new double[5];
            svmResultsWindow = new SVMResultsWindow();
            sumGradesInPredictFile = new double[5];
            for (int i = 0; i < gradesArrayFromOutFile.Count(); i++)
            {
                gradesArrayFromOutFile[i] = -1;
                gradesArrayFromPredictFile[i] = -1;
            }
            resetMatrixForComparingGrades();
            numOfVectorsToPredict = 0.0;
            maxAverageAccuracy.C = 0;
            maxAverageAccuracy.G = 0.0f;
            maxAverageAccuracy.AverageAccuracy = 0.0;
        }

        public static void runSVMtrain(double gamma, double c, string trainFilePath)
        {
            String command = "-s 0 -c " + c.ToString() + " -t 2 -g " + gamma.ToString() + " ";
            String[] commandArr = command.Split(' ');
            int lastIndex = commandArr.Count();
            commandArr[lastIndex-1] = trainFilePath;
            svm_train.SVMTrainMain(commandArr);

        }

        public static void runSVMPredict(string testFile, string modelFile, string outputFile)
        {
            String command = "-b 0   ";
            String[] commandArr = command.Split(' ');
            commandArr[2] = testFile;
            commandArr[3] = modelFile;
            commandArr[4] = outputFile;
            svm_predict.SVMPredictMain(commandArr);
        }

        public static void runSVMPredict(Object infoForPredict)
        {
            String command = "-b 0   ";
            String[] commandArr = command.Split(' ');
            MainWindow.InfoForPredict info = (MainWindow.InfoForPredict)infoForPredict;
            commandArr[2] = info.testFilePath;
            commandArr[3] = info.modelFilePath;
            commandArr[4] = info.outputFilePath;
            svm_predict.SVMPredictMain(commandArr);
            getGradeFromOutFileAndSendToExcel(info.outputFilePath,ref info.FileMgr);
        }

        public static void getGradeFromOutFileAndSendToExcel(String outputPath,ref FileMgr FileMgr)
        {
            StreamReader reader = new StreamReader(outputPath);
            String line = reader.ReadLine();
            String gradeString = line.Substring(1, 1);
            String[] filePath = outputPath.Split('.');
            String[] pathArray = outputPath.Split('\\');
            String testName = pathArray[4];
            int numOfTest = 0;
            if (testName == "Finger Taps")
            {
                numOfTest = 23;
            }
            else if (testName == "Hand Movements")
            {
                numOfTest = 25; //test number 24 , 25 because 23 contain left hand and right hand
            }
            reader.Close();
            FileMgr.updateGradeForTest(gradeString, filePath[0], numOfTest);

        }

        public void createListOfFilesForTrainAndPredict(SVMWindow.SVMParameters svmParameters)
        {
            currentTestName = svmParameters.testName;
            this.svmParameters = svmParameters;
            TrainAndPredictForSVM tempStruct = new TrainAndPredictForSVM();
            String pathOfFiles = MainWindow.startingPath + @"svm\";
            String fingerTapsPath = pathOfFiles + @"finger Taps\";
            String handMovementsPath = pathOfFiles + @"hand Movements\";
            DirectoryInfo fingerTapsDir = new DirectoryInfo(fingerTapsPath);
            DirectoryInfo handMovementsDir = new DirectoryInfo(handMovementsPath);
            FileInfo[] fingerTapsFiles = fingerTapsDir.GetFiles("*.text");
            FileInfo[] handMovementsFiles = handMovementsDir.GetFiles("*.text");
            switch (currentTestName)
            {
                case MainWindow.TestName.FINGER_TAPS:
                    for (int i = 0; i < fingerTapsFiles.Count() / 2; i++)
                    {
                        String predict = "predict" + i.ToString() + ".text";
                        String train = "train" + i.ToString() + ".text";
                        tempStruct.PredictPath = fingerTapsPath + predict;
                        tempStruct.TrainPath = fingerTapsPath + train;
                        SVMFilesList.Add(tempStruct);
                    }
                    break;
                
                case MainWindow.TestName.HAND_MOVEMENTS:
                    for (int i = 0; i < handMovementsFiles.Count() / 2; i++)
                    {
                        String predict = "predict" + i.ToString() + ".text";
                        String train = "train" + i.ToString() + ".text";
                        tempStruct.PredictPath = handMovementsPath + predict;
                        tempStruct.TrainPath = handMovementsPath + train;
                        SVMFilesList.Add(tempStruct);
                    }
                    break;
            }
        }

        public void crossValidation()
        {
            double sumOfAccuracy = 0;
            int files_index = 0;
            int parIndex = 0;
            resetMatrixForComparingGrades();
            int sizeOfArrayParameters = (Convert.ToInt32( ((svmParameters.gammaStart - svmParameters.gammaEnd) /
                                                           (svmParameters.gammaDecrement)) + 1) *
                                                           (Convert.ToInt32((svmParameters.cEnd - svmParameters.cStart)/svmParameters.cIncrement) + 1) );
            arrayOfSVMParameters = new LocalSVMParameters[sizeOfArrayParameters];
            removeParametersFolder(currentTestName);
            for (double g_index = svmParameters.gammaStart; g_index > svmParameters.gammaEnd; g_index -= svmParameters.gammaDecrement)
            {
                for (double c_index = svmParameters.cStart; c_index < svmParameters.cEnd; c_index += svmParameters.cIncrement)
                {
                    sumOfAccuracy = 0;
                    for (files_index = 0; files_index <  SVMFilesList.Count; files_index++ )
                    {
                        byte[] outFileBuffer = new byte[200];
                        runSVMtrain(g_index, c_index, SVMFilesList[files_index].TrainPath);
                        String folderForCAndG = createFoldersForCAndG(g_index, c_index, currentTestName);
                        String folderToMove = folderForCAndG + "\\train" + files_index.ToString() + ".text.model";
                        File.Copy(SVMFilesList[files_index].TrainPath + ".model", folderToMove, true);
                        File.Delete(SVMFilesList[files_index].TrainPath + ".model");
                        String modelFile = folderToMove;
                        String outputFile = folderForCAndG + "\\out" + files_index.ToString() + ".txt";
                        runSVMPredict(SVMFilesList[files_index].PredictPath, modelFile, outputFile);
                        FileStream outFile = new FileStream(outputFile, FileMode.Open);
                        outFile.Read(outFileBuffer, 0, (int)outFile.Length);
                        outFile.Close();
                        StreamReader predictFile = new StreamReader(SVMFilesList[files_index].PredictPath,true);
                        translateOutFileToArray(outFileBuffer);
                        translatePredictFileGradesToArray(predictFile);
                        sumOfAccuracy += compareGrades(gradesArrayFromOutFile, gradesArrayFromPredictFile);
                        predictFile.Close();
                    }
                    
                    if (m_updatePrograssBar != null)
                    {
                        m_updatePrograssBar(1);
                    }
                    
                    arrayOfSVMParameters[parIndex].G = g_index;
                    arrayOfSVMParameters[parIndex].C = c_index;
                    arrayOfSVMParameters[parIndex].AverageAccuracy = sumOfAccuracy/files_index;
                    if (arrayOfSVMParameters[parIndex].AverageAccuracy > maxAverageAccuracy.AverageAccuracy)
                    {
                        maxAverageAccuracy.G = arrayOfSVMParameters[parIndex].G;
                        maxAverageAccuracy.C = arrayOfSVMParameters[parIndex].C;
                        maxAverageAccuracy.AverageAccuracy = arrayOfSVMParameters[parIndex].AverageAccuracy;
                        confusionMatrixForCandGamma = (double[,]) tempConfusionMatrixForCandGamma.Clone();
                        sumGradeForConfusionMatrixForCandGamma = (double[]) tempSumGradeForConfusionMatrixForCandGamma.Clone();
                    }
                    parIndex++;
                    resetTempConfusionMatrixForCandGamma();
                    resetTempSumGradeForConfusionMatrixForCandGamma();

                }
                
            }
            //copyDataToSvmResultsWindowForAllCombinations();
            setParametersValueOnResultWindow();
            saveAllParametersToCsvFile();
            if (numOfClasses == 3)
            {
                svmResultsWindowThreeClasses.setAllCombinationsConfusionMatrix(matrixForComparingGrades,sumGradesInPredictFile);
                svmResultsWindowThreeClasses.setCAndGammaConfusionMatrix(confusionMatrixForCandGamma, sumGradeForConfusionMatrixForCandGamma);
                svmResultsWindowThreeClasses.setComboBoxLabel();
                svmResultsWindowThreeClasses.ShowDialog();
            }
            else //5 classes
            {
                svmResultsWindow.setAllCombinationsConfusionMatrix(matrixForComparingGrades, sumGradesInPredictFile);
                svmResultsWindow.setCAndGammaConfusionMatrix(confusionMatrixForCandGamma, sumGradeForConfusionMatrixForCandGamma);
                svmResultsWindow.setComboBoxLabel();
                svmResultsWindow.ShowDialog();
            }
            
            resetMatrixForComparingGrades();
            resetSumGradesInPredictFile();
            maxAverageAccuracy.G = 0;
            maxAverageAccuracy.C = 0;
            maxAverageAccuracy.AverageAccuracy = 0;
        }


       

        public String createFoldersForCAndG(double g, double c, MainWindow.TestName testName)
        {
            String path = "";
            switch (testName)
            {
                case MainWindow.TestName.FINGER_TAPS:
                    path = MainWindow.startingPath + "svm\\finger Taps\\Parameters\\" +
                           g.ToString() + " " + c.ToString();
                    
                    break;
                case MainWindow.TestName.HAND_MOVEMENTS:
                    path = (MainWindow.startingPath + "svm\\hand Movements\\Parameters\\" +
                            g.ToString() + " " + c.ToString());
                    break;
            }
            
            Directory.CreateDirectory(path);
            return path;
        }

        public void removeParametersFolder(MainWindow.TestName testName)
        {
            String path = "";
            switch (testName)
            {
                case MainWindow.TestName.FINGER_TAPS:
                    path = MainWindow.startingPath + "svm\\finger Taps\\Parameters\\" ;

                    break;
                case MainWindow.TestName.HAND_MOVEMENTS:
                    path = MainWindow.startingPath + "svm\\hand Movements\\Parameters\\";
                    break;
            }
            if (Directory.Exists(path))
            {
                DialogResult result = MessageBox.Show("Parameters folder is already exists. Do you want to remove it?", "SVM", MessageBoxButtons.YesNo,
                                MessageBoxIcon.Question);
                if (result == DialogResult.Yes)
                {
                    Directory.Delete(path, true);
                    do
                    {
                    } while (Directory.Exists(path));
                }
            }
        }

        public void translateOutFileToArray(byte[] outFileBuffer)
        {
            for (int i = 1; i < outFileBuffer.Length; i += 3)
            {
                gradesArrayFromOutFile[i/3] = Convert.ToInt32(outFileBuffer.GetValue(i)) - 48;
                if (gradesArrayFromOutFile[i/3] < 0)
                {
                    gradesArrayFromOutFile[i/3] = -1;
                    break;
                }
            }
        }

        public void translatePredictFileGradesToArray(StreamReader file)
        {
            int i = 0;
            while (!file.EndOfStream)
            {
                String line = file.ReadLine();
                gradesArrayFromPredictFile[i] = Convert.ToInt32(line.First()) - 48;
                if (gradesArrayFromPredictFile[i] < 0)
                {
                    gradesArrayFromPredictFile[i] = -1;
                }
                i++;
            }
            numOfVectorsToPredict = i;
        }

        public double compareGrades(Int32[] gradesFromOut, Int32[] gradesFromPredict)
        {
            double equal = 0;
            double accuracy = 0.0;
            for (int i = 0; i < numOfVectorsToPredict; i++ )
            {
                if (gradesFromOut[i] == gradesFromPredict[i])
                {
                    equal+=1.0;
                }
                if (numOfClasses == 3)
                {
                    int j = 0;
                    int k = 0;

                    if (gradesFromOut[i] == 0)
                    {
                        j = 0;
                    }
                    else if (gradesFromOut[i] == 1)
                    {
                        j = 1;
                    }
                    else if (gradesFromOut[i] == 3)
                    {
                        j = 2;
                    }
                    if (gradesFromPredict[i] == 0)
                    {
                        k = 0;
                    }
                    else if (gradesFromPredict[i] == 1)
                    {
                        k = 1;
                    }
                    else if (gradesFromPredict[i] == 3)
                    {
                        k = 2;
                    }
                    matrixForComparingGrades[j, k]++;
                    sumGradesInPredictFile[k]++;
                    tempConfusionMatrixForCandGamma[j, k]++;
                    tempSumGradeForConfusionMatrixForCandGamma[k]++;
                }
                else // 5 classes
                {
                    matrixForComparingGrades[gradesFromOut[i], gradesFromPredict[i]]++;
                    sumGradesInPredictFile[gradesFromPredict[i]]++; 
                    tempConfusionMatrixForCandGamma[gradesFromOut[i],gradesFromPredict[i]]++;
                    tempSumGradeForConfusionMatrixForCandGamma[gradesFromPredict[i]]++;
                }
                
            }
            accuracy = equal/numOfVectorsToPredict;
            return accuracy;
        }

        public void setNumOfClasses(int numOfClasses)
        {
            this.numOfClasses = numOfClasses;
        }

        public void resetMatrixForComparingGrades()
        {
            for (int i = 0; i < 5; i++)
                for (int j = 0; j < 5; j++)
                    matrixForComparingGrades[i, j] = 0;
        }

        public void resetSumGradesInPredictFile()
        {
            for (int i = 0; i < sumGradesInPredictFile.Count(); i++)
                sumGradesInPredictFile[i] = 0.0;
        }

        public void resetTempConfusionMatrixForCandGamma()
        {
            for (int i = 0; i < 5; i++)
                for (int j = 0; j < 5; j++)
                    tempConfusionMatrixForCandGamma[i, j] = 0;
        }

        public void resetTempSumGradeForConfusionMatrixForCandGamma()
        {
            for (int i = 0; i < sumGradesInPredictFile.Count(); i++)
                tempSumGradeForConfusionMatrixForCandGamma[i] = 0.0;
        }

        public void setParametersValueOnResultWindow()
        {
            if (numOfClasses == 3)
            {
                svmResultsWindowThreeClasses.gammaValueLabel.Text = maxAverageAccuracy.G.ToString();
                svmResultsWindowThreeClasses.cValueLabel.Text = maxAverageAccuracy.C.ToString();
                svmResultsWindowThreeClasses.averageValueLabel.Text = (maxAverageAccuracy.AverageAccuracy * 100).ToString() + "%";
            }
            else //5 classes
            {
                svmResultsWindow.gammaValueLabel.Text = maxAverageAccuracy.G.ToString();
                svmResultsWindow.cValueLabel.Text = maxAverageAccuracy.C.ToString();
                svmResultsWindow.averageValueLabel.Text = (maxAverageAccuracy.AverageAccuracy * 100).ToString() + "%";
            }
            
            StreamWriter file;
            String fileName = "MaxSVMParams.csv";
            String path = "";
            switch (currentTestName)
            {
                case MainWindow.TestName.FINGER_TAPS:
                    path = "svm\\finger Taps\\Parameters\\";
                    break;

                case MainWindow.TestName.HAND_MOVEMENTS:
                    path = "svm\\hand Movements\\Parameters\\";
                    break;
            }
            String firstLine = "MaxGamma,MaxC,MaxAverageAcuuracy";
            String secondLine = maxAverageAccuracy.G.ToString() + "," + maxAverageAccuracy.C.ToString() + "," +
                                (maxAverageAccuracy.AverageAccuracy*100).ToString();
            file = new StreamWriter(MainWindow.startingPath + path + fileName, false);
            file.WriteLine(firstLine);
            file.WriteLine(secondLine);
            file.Close();
        }

        public void saveAllParametersToCsvFile()
        {
            StreamWriter file;
            String fileName = "AllSVMParams.csv";
            String path = "";
            String line = "";
            switch (currentTestName)
            {
                case MainWindow.TestName.FINGER_TAPS:
                    path = "svm\\finger Taps\\Parameters\\";
                    break;

                case MainWindow.TestName.HAND_MOVEMENTS:
                    path = "svm\\hand Movements\\Parameters\\";
                    break;
            }
            file = new StreamWriter(MainWindow.startingPath + path + fileName);
            String firstLine = "Gamma,C,AverageAcuuracy";
            file.WriteLine(firstLine);
            for (int i = 0; i < arrayOfSVMParameters.Count(); i++)
            {
                if (arrayOfSVMParameters[i].G == 0 && arrayOfSVMParameters[i].C == 0 && arrayOfSVMParameters[i].AverageAccuracy == 0)
                    break;
                line = arrayOfSVMParameters[i].G.ToString() + "," + arrayOfSVMParameters[i].C.ToString() + "," +
                       (arrayOfSVMParameters[i].AverageAccuracy*100).ToString();
                file.WriteLine(line);
                line = "";
            }
            file.Close();
        }

    }
}
